<?php
/**
 * Celebrio Core
 * 
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage Core
 */
namespace Celebrio\Core;

use Nette\Object;
use Nette\String;
use dibi;

use Celebrio\Modules;
use Celebrio\Core\IModuleInit;

use Celebrio\FileSystem\Folder;

/**
 * Dummy ModuleInit implementation, without install or init, always available,
 * not changing the config, without config form fields.
 */
class DummyInit extends Object implements IModuleInit {

    const UP = "up";
    const DOWN = "down";
    
    public function install(array $configuration) {

        $sqlDirName = dirname($this->getReflection()->getFileName()) . "/sql/";
        if (is_dir($sqlDirName)) {
            $this->findAndRunDDL($sqlDirName, self::UP);
        }
    }

    public function uninstall() {
        $sqlDirName = dirname($this->getReflection()->getFileName()) . "/sql/";
        if (is_dir($sqlDirName)) {
            $this->findAndRunDDL($sqlDirName, self::DOWN);
        }
    }
    
    public function init(array $configuration) {
        // no init
    }
    
    public function isAvailable() {
        // always available
        return true;
    }
    
    public function formFields() {
        // no config form
        return array();
    }
    
    public function changeConfig(array $configuration) {
        // no changes in config
        return $configuration;
    }
    
    /**
     * Executes SQL script belonging to the database engine
     * which is currently running.
     * 
     * Installation SQL script should be
     * named as "MODULE_DBNAME.sql", where:
     *      - MODULE is moudle abbreviation, it can be any string
     *      - DBNAME is database name, corresponding to the name
     *        returned by service provider getServiceName() method.
     *        Known DBNAMEs are "Postgre", "MySQL", "MSSQL" and "Azure".
     * 
     * @param String $sqlDirName
     */
    protected function findAndRunDDL($sqlDirName, $direction) {

        // add slash to the end if it's not present
        if (!String::endsWith($sqlDirName, "/")) {
            $sqlDirName .= "/";
        }

        // get SQL directory contents, filter only *.sql files
        $sqlDir = new Folder($sqlDirName);
        $contents = $sqlDir->getContent();
        $function = null;
        switch($direction) {
            case self::UP :
                $function = function ($element) { return (String::endsWith($element, ".sql") && !String::endsWith($element, "." . DummyInit::DOWN . ".sql")); };
                break;
            case self::DOWN :
                $function = function ($element) { return (String::endsWith($element, "." . DummyInit::DOWN . ".sql")); };
                break;
            default : throw new \Exception("Unknown direction given (only UP and DOWN are available)");

        }

        $contents = array_filter($contents, $function);
        //\Nette\Debug::dump($contents);
        if (empty($contents)) {
            // there are no scripts for the requested direction (up or down).
            if ($direction === self::DOWN) return;
            else throw new \Exception("SQL directory without SQL scripts found. That's unacceptable.");
        }

        // retrieve module SQL prefix
        $prefix = join("_", explode("_", end($contents), "-1"));

        // execute SQL script
        $db_name = ServiceProvider::getServiceName(Modules::DB);
        $db_name = $this->changeAzureForMssql($db_name, $contents, $prefix);


        dibi::begin();
        DDL::execute($sqlDirName . $prefix . "_" . $db_name . ($direction === self::DOWN ? ("." . self::DOWN) : "") . ".sql");
        dibi::commit();
    }

    /**
     * Temporary hack for MSSQL and Azure. If we run Azure database,
     * Azure script is missing and MSSQL script is present, it's used instead.
     * This method returns the input for all other databases.
     *
     * @param string $db_name Database name, for example Azure or Postgres
     * @param array $contents
     * @param $prefix
     * @return string
     */
    private function changeAzureForMssql($db_name, array $contents, $prefix) {
        $azure_name = "Azure";
        $mssql_name = "MSSQL";
        if ($db_name == $azure_name) {
            $azure_present = false;
            foreach ($contents as $sql_script) {
                if ($sql_script == $prefix . "_" . $azure_name . ".sql") {
                    $azure_present = true;
                    break;
                }
            }
            if (!$azure_present) {
                // use MSSQL instead
                $db_name = $mssql_name;
            }
        }
        return $db_name;
    }
}